﻿#region Usings

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media.Imaging;

using Gonte.Algorithms;
using Gonte.Wpf.Controls.ResourceManagers; 

#endregion

namespace Gonte.Wpf.Controls.ViewModel
{
    /// <summary>
    /// The base class to implement the view model pattern
    /// </summary>
    public abstract class ViewModelBase
        : INotifyPropertyChanged
        , IViewModel
        , IComparable
    {

        #region Constructors

        protected ViewModelBase(ViewModelBase parent, bool loadOnDemand)
        {
            _parent = parent;
            children_ = new ObservableCollection<IViewModel>();

            if (loadOnDemand)
            {
                children_.Add(LoadOnDemandViewModel.Instance);
            }
        }

        #endregion

        #region Properties

        /// <summary>
        /// The binding to this view model
        /// </summary>
        public ReadOnlyCollection<ViewModelBase> This
        {
            get
            {
                return new ReadOnlyCollection<ViewModelBase>
                (
                    new ViewModelBase[] { this }
                );
            }
        }

        public ViewModelBase Parent
        {
            get { return _parent; }
            set { _parent = value; }
        }

        public ObservableCollection<IViewModel> Children
        {
            get { return children_; }

            set 
            {
                children_ = value;
                OnPropertyChanged("Children");
            }
        }

        public bool IsLoadedOnDemand
        {
            get 
            {
                return (Children.Count == 1
                    && Children[0] == LoadOnDemandViewModel.Instance); 
            }
        }

        public bool IsExpanded
        {
            get
            {
                return isExpanded_;
            }

            set
            {
                if (value != isExpanded_)
                {
                    isExpanded_ = value;
                    OnPropertyChanged("IsExpanded");
                }

                if (_parent != null
                    && isExpanded_)
                {
                    _parent.IsExpanded = true; // Propagate to the root
                }

                if (IsLoadedOnDemand)
                {
                    Children.Remove(LoadOnDemandViewModel.Instance);
                    LoadChildren();
                }
            }
        }

        public bool IsSelected
        {
            get 
            { 
                return isSelected_; 
            }

            set
            {
                if (value != isSelected_)
                {
                    //parent_.selectedChild_ = this;
                    isSelected_ = value;
                    OnPropertyChanged("IsSelected");
                }
            }
        }

        public bool IsChecked
        {
            get 
            { 
                return isChecked_; 
            }

            set 
            {
                if (value != isChecked_)
                {
                    isChecked_ = value;
                    OnPropertyChanged("IsChecked");

                    // Propagate down to the other children
                    foreach (ViewModelBase childViewModel in children_)
                    {
                        childViewModel.IsChecked = isChecked_;
                    }
                }
            }
        }

        public Visibility Visibility
        {
            get 
            { 
                return visibility_; 
            }

            set 
            {
                if (value != visibility_)
                {
                    visibility_ = value;
                    OnPropertyChanged("Visibility");
                }
            }
        }

        /// <summary>
        /// Reloads all the children of this treeview item when there is a change
        /// </summary>
        public bool ReloadOnChildrenChange
        {
            get { return reloadOnChildrenChange_; }
            set { reloadOnChildrenChange_ = value; }
        }

        public string CollapsedIconUri
        {
            get { return _collapsedIconUri; }
            set { _collapsedIconUri = value; }
        }

        public string ExpandedIconUri
        {
            get { return _expandedIconUri; }
            set { _expandedIconUri = value; }
        }

        /// <summary>
        /// The path to the icon that shows up on the tree view item
        /// </summary>
        public BitmapImage IconSource
        {
            get 
            { 
                return iconSource_; 
            }

            set 
            {
                if (value != iconSource_)
                {
                    iconSource_ = value;
                    OnPropertyChanged("IconSource");
                }
            }
        }

        public String SearchText
        {
            get { return searchText_; }

            set 
            {
                if (
                    (searchText_ == null && !String.IsNullOrEmpty(value))
                    || (searchText_ != null && !searchText_.Equals(value))
                    )
                {
                    searchText_ = value;
                    PopulateMatchEnumerator();
                }
            }
        }

        #endregion

        #region Methods

        public ViewModelBase FindChild(Object child)
        {
            foreach (ViewModelBase viewModel in children_)
            {
                if (viewModel.Model.Equals(child))
                {
                    return viewModel;
                }
            }

            return null;
        }

        public void Search()
        {
            if (matchEnumerator_ == null
                || !matchEnumerator_.MoveNext())
            {
                IEnumerable<ViewModelBase> matches = FindMatches(searchText_, this);
                matchEnumerator_ = matches.GetEnumerator();

                if (!matchEnumerator_.MoveNext())
                {
                    MessageBox.Show(
                        "No matching names were found.",
                        "Try Again",
                        MessageBoxButton.OK,
                        MessageBoxImage.Information
                        );
                }
            }

            ViewModelBase viewModel = matchEnumerator_.Current;

            if (viewModel == null)
            {
                return;
            }

            // Ensure that this item is in view.
            if (viewModel.Parent != null)
            {
                viewModel.Parent.IsExpanded = true;
            }

            viewModel.IsSelected = true;
        }

        IEnumerable<ViewModelBase> FindMatches(String searchText, ViewModelBase viewModel)
        {
            if (viewModel.Matches(searchText))
            {
                yield return viewModel;
            }

            foreach (ViewModelBase child in viewModel.Children)
            {
                foreach (ViewModelBase matchViewModel in this.FindMatches(searchText, child))
                {
                    yield return matchViewModel;
                }
            }
        }

        #endregion

        #region Overridables

        public abstract String Label { get; set; }

        public abstract Object Model { get; set; }

        public abstract ContextMenu CreateContextMenu();

        public abstract void OnPropertyChanged(String propertyName, Object oldValue);

        protected void UpdateIconSource()
        {
            string imageUri = null;
            if (IsExpanded)
            {
                imageUri = _expandedIconUri;
            }
            else
            {
                imageUri = _collapsedIconUri;
            }

            iconSource_ = ImageManager.GetImage(imageUri);
            OnPropertyChanged("IconSource");
        }

        public virtual void LoadChildren()
        {
            if (sortChildren_)
            {
                //ShellSorter<ViewModelBase>.Sort(children_);
            }
        }

        public virtual bool AddChild(ViewModelBase viewModel)
        {
            // Expand and select the added view model
            viewModel.IsExpanded = true;
            viewModel.IsSelected = true;

            children_.Add(viewModel);
            if (sortChildren_)
            {
                //ShellSorter<ViewModelBase>.Sort(children_);
            }

            OnPropertyChanged("Children");

            return true;
        }

        public virtual void InsertChild(int index, ViewModelBase child)
        {
            // Expand and select the added viewModel
            child.IsExpanded = true;
            child.IsSelected = true;

            children_.Insert(index, child);

            OnPropertyChanged("Children");
        }

        public virtual bool RemoveChild(ViewModelBase viewModel)
        {
            bool removed = children_.Remove(viewModel);

            if (!removed)
            {
                return removed;
            }

            int count = children_.Count;
            if (count == 0) // Expand and select the parent
            {
                // IsExpanded = true;
                IsSelected = true;
            }
            else
            {
                children_[count - 1].IsSelected = true;
            }

            OnPropertyChanged("Children");

            return removed;
        }

        public virtual bool RemoveSelectedChild()
        {
            return RemoveChild(this);
        }

        public virtual bool MoveChildUp(ViewModelBase item, out int newIndex)
        {
            int oldIndex = children_.IndexOf(item);
            newIndex = oldIndex - 1;

            if (oldIndex == 0)
            {
                return false; // It is the first already
            }

            children_.Move(oldIndex, newIndex);
            OnPropertyChanged("Children");

            return true;
        }

        public virtual bool MoveChildDown(ViewModelBase item, out int newIndex)
        {
            int oldIndex = children_.IndexOf(item);
            if (oldIndex == children_.Count - 1)
            {
                newIndex = -1;
                return false; // It is the last already
            }

            newIndex = oldIndex + 1;
            children_.Move(oldIndex, newIndex);
            OnPropertyChanged("Children");

            return true;
        }

        protected virtual bool Matches(String searchText)
        {
            return Label.Contains(searchText_);
        }

        #endregion

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(String propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }

            if (propertyName.Equals("Children")
                && reloadOnChildrenChange_)  // Force a reload
            {
                ReloadChildren();
            }

            //UpdateIconSource();
        }

        protected virtual void ReloadChildren()
        {
            children_.Clear();
            LoadChildren();
        }

        #endregion

        #region IComparable Members

        public abstract int CompareTo(Object @object);

        #endregion

        #region Event handlers

        #region Selection changed

        internal void OnSelectionChanged(object sender, ViewModelBase viewModel)
        {
            ViewModelBase root = FindRoot();

            if (null != root._selectionChanged)
            {
                root._selectionChanged(this, new ViewModelEventArgs(viewModel));
            }
        }

        public SelectionChangedHandler SelectionChanged
        {
            get { return _selectionChanged; }
            set { _selectionChanged = value; }
        }

        public delegate void SelectionChangedHandler(object sender, ViewModelEventArgs args);

        private SelectionChangedHandler _selectionChanged;

        #endregion

        #endregion

        #region Helpers

        private ViewModelBase FindRoot()
        {
            if (null == _parent)
            {
                return this;
            }

            return _parent.FindRoot();
        }

        public static void AddMenuItem(ContextMenu menu, ICommand command)
        {
            MenuItem item = new MenuItem();
            item.Command = command;
            menu.Items.Add(item);
        }

        public static void AddMenuItem(ContextMenu menu, string header, ICommand command)
        {
            MenuItem item = new MenuItem();
            item.Header = header;
            item.Command = command;
            menu.Items.Add(item);
        } 

        private void PopulateMatchEnumerator()
        {
            throw new NotImplementedException();
        }

        #endregion

        #region Fields

        private ViewModelBase _parent;
        private ObservableCollection<IViewModel> children_;
        private bool isExpanded_;
        private bool isSelected_;
        private bool isChecked_;
        private Visibility visibility_;

        // Icon
        private string _collapsedIconUri;
        private string _expandedIconUri;
        private BitmapImage iconSource_;

        private bool sortChildren_ = false;
        private bool reloadOnChildrenChange_ = false;

        // Searching
        private String searchText_;
        private IEnumerator<ViewModelBase> matchEnumerator_;

        #endregion
    }
}
